-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathserver.js
More file actions
93 lines (84 loc) · 2.91 KB
/
server.js
File metadata and controls
93 lines (84 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import Fastify from "fastify";
import fastifyWs from "@fastify/websocket";
import fastifyFormBody from "@fastify/formbody";
import OpenAI from "openai";
import dotenv from "dotenv";
dotenv.config();
const PORT = process.env.PORT || 8080;
const DOMAIN = process.env.NGROK_URL;
const WS_URL = `wss://${DOMAIN}/ws`;
const WELCOME_GREETING =
"Hi! I am a voice assistant powered by Twilio and Open A I . Ask me anything!";
const SYSTEM_PROMPT =
"You are a helpful assistant. This conversation is being translated to voice, so answer carefully. When you respond, please spell out all numbers, for example twenty not 20. Do not include emojis in your responses. Do not include bullet points, asterisks, or special symbols.";
const sessions = new Map();
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function aiResponse(messages) {
let completion = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: messages,
});
return completion.choices[0].message.content;
}
const fastify = Fastify();
fastify.register(fastifyWs);
fastify.register(fastifyFormBody);
fastify.all("/twiml", async (request, reply) => {
reply.type("text/xml").send(
`<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Connect>
<ConversationRelay url="${WS_URL}" welcomeGreeting="${WELCOME_GREETING}" />
</Connect>
</Response>`
);
});
fastify.register(async function (fastify) {
fastify.get("/ws", { websocket: true }, (ws, req) => {
ws.on("message", async (data) => {
const message = JSON.parse(data);
switch (message.type) {
case "setup":
const callSid = message.callSid;
console.log("Setup for call:", callSid);
ws.callSid = callSid;
sessions.set(callSid, [{ role: "system", content: SYSTEM_PROMPT }]);
break;
case "prompt":
console.log("Processing prompt:", message.voicePrompt);
const conversation = sessions.get(ws.callSid);
conversation.push({ role: "user", content: message.voicePrompt });
const response = await aiResponse(conversation);
conversation.push({ role: "assistant", content: response });
ws.send(
JSON.stringify({
type: "text",
token: response,
last: true,
})
);
console.log("Sent response:", response);
break;
case "interrupt":
console.log("Handling interruption.");
break;
default:
console.warn("Unknown message type received:", message.type);
break;
}
});
ws.on("close", () => {
console.log("WebSocket connection closed");
sessions.delete(ws.callSid);
});
});
});
try {
fastify.listen({ port: PORT });
console.log(
`Server running at http://localhost:${PORT} and wss://${DOMAIN}/ws`
);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}